#include "WorldObject.h"

/*******************************************************************
					PUBLIC Class Functions
*******************************************************************/


// Release resources
void WorldObject::Release() {
	mesh.Release();
	meshInst.Release();
}

// Initialize variables to their default values
bool WorldObject::Initialize(LPDIRECT3DDEVICE9 pDevice) {
	maxHealth = 10.0f;
	health = maxHealth;
	hpRegen = 0.0f;
	hpRegenTimer = 0.0f;
	//key = 0;
	
	// lari
	static int woInitCounter = 0;
	key = woInitCounter;
	PRINT("key = %d\n", woInitCounter);
	woInitCounter++;
	
	// endlari
	value = 0;
	distToCam = 0.0f;
	alignment = "Neutral";
	dataFile = " ";
	if(modelFile.empty())
		modelFile = " ";
	name = "Unnamed";
	InitializeUnitInfo();
	InitializePhysicsInfo();
	weaIter = weapons.begin();
	
	m_state = wandering;
	m_targetUnit = NULL;
	m_targetNonUnit = NULL;

	// Initialize mesh
	if(!InitializeMesh(pDevice))
		return false;

	return true;
}

// Initialize the mesh
bool WorldObject::InitializeMesh(LPDIRECT3DDEVICE9 pDevice) {
	if(modelFile != " ") {
		//PRINT("Reading from model file: ");
		//PRINT(modelFile.c_str());	NEXT_LINE;
		if(!mesh.Load(pDevice, modelFile)) {
			SHOWERROR("mesh.Load() - Failed", __FILE__, __LINE__);
			return false;
		}
		meshInst.setMesh(&mesh);
		float radius = 0.0f;
		radius = (meshInst.getXScale() + meshInst.getYScale() + meshInst.getZScale()) / g_BoundingRadiusModifier;
		radius *= meshInst.getBoundingRadius();
		meshInst.setBoundingRadius(radius);
	} else {
		PRINT("Model file name not specified, current entry: ");
		PRINT(modelFile.c_str());	NEXT_LINE;
		return false;
	}

	return true;
}

// General Update function, mainly for the A.I.
void WorldObject::Update(float elapsedTime) {
	// Self-explanatory functions
//	UpdateLookDirection(elapsedTime);
	ConstrictDirectionPoint();				// forces the dirPoint to be within a certain distance from the object's position
	UpdateVelocityVector(elapsedTime);
	UpdateMovement(elapsedTime);
	UpdateMeshRotation();
	UpdateWeaponCooldown(elapsedTime);
	UpdateHpRegen(elapsedTime);
}

// Update function, mainly for the Player as it contains mouse delta information
void WorldObject::Update(bool TDView, float deltaX, float deltaY, float elapsedTime) {
	// Self-explanatory functions
	if(TDView)
		MoveRelativeToMouse(deltaX,deltaY,elapsedTime);	// updates the dirPoint relative to the mouse deltas
	else
		MoveRelativeToMouseX(deltaX,elapsedTime);
	UpdateLookDirection(elapsedTime);
	ConstrictDirectionPoint();						// forces the dirPoint to be within a certain distance from the object's position
	UpdateVelocityVector(elapsedTime);
	UpdateMovement(elapsedTime);
	UpdateMeshRotation();
	UpdateWeaponCooldown(elapsedTime);
	UpdateHpRegen(elapsedTime);
}

// Render the object
void WorldObject::Render(LPDIRECT3DDEVICE9 pDevice) {
	meshInst.Render(pDevice);
}

// Update the direction the object is looking in, then normalize it
void WorldObject::UpdateLookDirection(float elapsedTime) {
	physicsInfo.dirLook = physicsInfo.dirPoint - *meshInst.getPosition();
	physicsInfo.dirLook = *D3DXVec3Normalize(&physicsInfo.dirLook, &physicsInfo.dirLook);
}

// Limit the dirPoint to a certain distance from the object's position (to make sure that the turning speeds will affect
// every object in the same manner.
void WorldObject::ConstrictDirectionPoint() {
	physicsInfo.dirPoint = (*meshInst.getPosition()) +  (physicsInfo.dirLook * g_playerDestDist);
}

/*******************************************************************
					PRIVATE Class Functions
*******************************************************************/


// Initialize the Unit information
void WorldObject::InitializeUnitInfo() {
	unitInfo.canMove = true;
	unitInfo.group = 0;
	unitInfo.level = 1;
	unitInfo.LOS = 50.0f;
	unitInfo.shootRange = unitInfo.LOS * g_ShootingRangeModifier;
	if(unitInfo.shootRange <= 1.0f) {
		unitInfo.shootRange = 2.0f;
	}
	unitInfo.stance = "Aggressive";
	unitInfo.status = "Holding";
}

// Initialize the Physics information
void WorldObject::InitializePhysicsInfo() {
	physicsInfo.destination = D3DXVECTOR3(0,0,0);
	physicsInfo.dirLook = D3DXVECTOR3(0,0,0);
	physicsInfo.dirPoint = D3DXVECTOR3(0,0,0);
	physicsInfo.velocity = D3DXVECTOR3(0,0,0);
	physicsInfo.turningSpeed = 0.1f;
	physicsInfo.maxTurningSpeed = 1.0f;
	physicsInfo.maxVelocity = 0.2f;
}

// Update the object's movement
void WorldObject::UpdateMovement(float elapsedTime) {
	if(unitInfo.canMove) {
		meshInst.translateRel(physicsInfo.velocity.x, physicsInfo.velocity.y, physicsInfo.velocity.z);
	}
}

// Move the dirPoint relative to the mouse deltas
void WorldObject::MoveRelativeToMouse(float deltaX, float deltaY, float elapsedTime) {
	// If there is a change in mouseX...
	if(deltaX != 0) {
		// if the change is greater than the maximum turning speed of this object...
		if(abs(deltaX * physicsInfo.turningSpeed) > physicsInfo.maxTurningSpeed) {
			// set the change to the max, depending on whether it is going left or right
			if(deltaX > 0) {
				physicsInfo.dirPoint.x += physicsInfo.maxTurningSpeed;
			} else {
				physicsInfo.dirPoint.x -= physicsInfo.maxTurningSpeed;
			}
		// else, set dirPoint's relative offset to the delta multiplied by the default turning speed (x-axis)
		} else {
			physicsInfo.dirPoint.x += (deltaX * physicsInfo.turningSpeed);
		}
	}
	// If there is a change in mouseY...
	if(deltaY != 0) {
		// if the change is greater than the maximum turning speed of this object...
		if(abs(deltaY * physicsInfo.turningSpeed) > physicsInfo.maxTurningSpeed) {
			// set the change to the max, depending on whether it is going up or down
			if(deltaY > 0) {
				physicsInfo.dirPoint.z -= physicsInfo.maxTurningSpeed;
			} else {
				physicsInfo.dirPoint.z += physicsInfo.maxTurningSpeed;
			}
		// else, set dirPoint's relative offset to the delta multiplied by the default turning speed (y-axis mouse, z-axis world)
		} else {
			physicsInfo.dirPoint.z -= (deltaY * physicsInfo.turningSpeed);
		}
	}
}

void WorldObject::MoveRelativeToMouseX(float deltaX, float elapsedTime) {
	D3DXVECTOR3 dir(physicsInfo.dirLook);		D3DXVECTOR3 pDir;
	int quadrant = 0;		bool right = false;		bool delta = false;

	if(deltaX > 0.0f) {
		right = true;
		delta = true;
	} else if(deltaX < 0.0f) {
		right = false;
		delta = true;
	}

	if(delta) {
		if(dir.x >= 0.0f && dir.z >= 0.0f) {
			quadrant = 1;
		} else if(dir.x <= 0.0f && dir.z >= 0.0f) {
			quadrant = 2;
		} else if(dir.x <= 0.0f && dir.z <= 0.0f) {
			quadrant = 3;
		} else {
			quadrant = 4;
		}
		
		if(right) {
			pDir.x = dir.z;
			pDir.z = -dir.x;
		} else {
			pDir.x = -dir.z;
			pDir.z = dir.x;
		}
		pDir.y = 0.0f;
		
		if(abs((deltaX * physicsInfo.turningSpeed)) > physicsInfo.maxTurningSpeed) {
			physicsInfo.dirPoint += (pDir * physicsInfo.maxTurningSpeed);
		} else {
			if(deltaX > 0.0f)
				physicsInfo.dirPoint += (pDir * (deltaX*physicsInfo.turningSpeed));
			else
				physicsInfo.dirPoint -= (pDir * (deltaX*physicsInfo.turningSpeed));
		}
	}
}

// Update the velocity vector. The object will always travel at a constant speed...will be in constant motion.
void WorldObject::UpdateVelocityVector(float elapsedTime) {
	if(unitInfo.canMove) {
		physicsInfo.velocity = *(D3DXVec3Normalize(&physicsInfo.velocity, &physicsInfo.dirLook)) * (physicsInfo.maxVelocity * elapsedTime);
	}
}
 
// Update the object mesh's rotation in the x and z axis
// Dependent on the direction point of the object
void WorldObject::UpdateMeshRotation() {
	D3DXVECTOR3 origin(0,0,0);		D3DXVECTOR3 point(physicsInfo.dirPoint);
	int quadrant = 0;				float angle = 0;
	point = point - *meshInst.getPosition();

	if(point.x > 0 && point.z > 0) {
		angle = acos((float)abs((point.x / calDist(&point,&origin)))) + ToRad(90);
		meshInst.rotateAbs(0.0f,-angle,0.0f);
	} else if(point.x > 0 && point.z < 0) {
		angle = acos((float)abs((point.z / calDist(&point,&origin))));
		meshInst.rotateAbs(0.0f,-angle,0.0f);
	} else if(point.x < 0 && point.z > 0) {
		angle = acos((float)abs((point.x / calDist(&point,&origin)))) + ToRad(90);
		meshInst.rotateAbs(0.0f,angle,0.0f);
	} else if(point.x < 0 && point.z < 0) {
		angle = acos((float)abs((point.z / calDist(&point,&origin))));
		meshInst.rotateAbs(0.0f,angle,0.0f);
	}
}

// If any weapons are still on cooldown, update them
void WorldObject::UpdateWeaponCooldown(float elapsedTime) {
	for(weaIter = weapons.begin(); weaIter != weapons.end(); weaIter++) {
		if(weaIter->fireTime < weaIter->fireRate)
			weaIter->fireTime += elapsedTime;
	}
}

void WorldObject::UpdateHpRegen(float elapsedTime) {
	if(health <= 0.0f) {
	} else {
		if(hpRegenTimer >= g_HpRegenTimerMax) {
			hpRegenTimer = 0;
			health += hpRegen;
			if(health > maxHealth)
				health = maxHealth;
		} else {
			hpRegenTimer += elapsedTime;
		}
	}
}
